﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HAMath
{
    /// <summary>
    /// 实验变差函数
    /// </summary>
    [Serializable]
    public class ExperimentalVariogram : ICloneable
    {

        public ExperimentalVariogram(VariogramType VariogramType)
        {
            this._variogramType = VariogramType;
        }

        

        //离散点，给定所有点坐标
        //给定所有点的值（类似浓度值、高程值）

        //下面这些字段以后都可能是要放到函数的参数里面的
        //现在先以字段的形式放着，看以后的结构吧
        //而且这些字段是可能通过计算提供给用户一个默认值的，然后再供用户修改。计算默认值的方法以后再考虑，目前看来是不难


        private VariogramType _variogramType = VariogramType.Semivariogram;

        /// <summary>
        /// 变差函数类型
        /// </summary>
        public VariogramType VariogramType
        {
            get { return _variogramType; }
            set { _variogramType = value; }
        }


        private string _name = "实验变差函数";

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }


        private double _lagDistance=100;  //滞后距。这个需是正数，不能小于等于0

        /// <summary>
        /// /滞后距。这个需是正数，不能小于等于0
        /// </summary>
        public double LagDistance
        {
            get { return _lagDistance; }
            set 
            {
                if (value <= 0)
                {
                    value = 1;
                }
                _lagDistance = value;
            }
        }


        private double _lagTolerance=50;  //滞后误差。这个值默认一般采用滞后距的一半，从GMS中获得

        /// <summary>
        /// 滞后误差。这个值默认一般采用滞后距的一半，从GMS中获得
        /// </summary>
        public double LagTolerance
        {
            get { return _lagTolerance; }
            set 
            {
                if (value <= 0)
                {
                    value = 1;
                }
                _lagTolerance = value; 
            }
        }


        private int _lagNumber=10;  //滞后数。其实就是采样点数

        /// <summary>
        /// 滞后数。其实就是采样点数
        /// </summary>
        public int LagNumber
        {
            get { return _lagNumber; }
            set 
            {
                if (value <= 0)
                {
                    value = 1;
                }
                _lagNumber = value; 
            }
        }

        private double _angle = 0;  //这个字段现在理解就是：不管二维、三维都是给定一个搜索方向，由用户指定。而不是X Y Z方向

        /// <summary>
        /// 不管二维、三维都是给定一个搜索方向，由用户指定
        /// </summary>
        public double Angle
        {
            get { return _angle; }
            set { _angle = value; }
        }


        private double _angleTolerance = 180 / 8;  //方位角误差。这个默认值是从文档中获得

        /// <summary>
        /// 方位角误差
        /// </summary>
        public double AngleTolerance
        {
            get { return _angleTolerance; }
            set { _angleTolerance = value; }
        }


        private double _bandWidth=100;  //带宽

        /// <summary>
        /// 带宽
        /// </summary>
        public double BandWidth
        {
            get { return _bandWidth; }
            set 
            {
                if (value <= 0)
                {
                    value = 10;
                }
                _bandWidth = value;
            }
        }


        //各向异向还没想通，暂时先不考虑吧

        //我认为：在二维中就考虑两个方向，不要考虑三个方向，也不是一个方向……
        //上面这句是错的：不管是在一维 、二维 、三维，都只考虑一个方向，由用户指定。。。这是目前的理解
        //三维中才考虑三个方向


        double[] _variation;

        /// <summary>
        /// 结果变差值
        /// </summary>
        public double[] Variation
        {
            get { return _variation; }
            set { _variation = value; }
        }


        double[] _averageDistance;

        /// <summary>
        /// 结果距离
        /// </summary>
        public double[] AverageDistance
        {
            get { return _averageDistance; }
            set { _averageDistance = value; }
        }


        /// <summary>
        /// 计算出变差函数值
        /// </summary>
        /// <param name="VariogramType">变差函数类型</param>
        public void CalVariation(List<KrigingPoint> Points, List<double> Values)
        {
            //double[] Variation = new double[this.LagNumber];

            switch (VariogramType)
            {
                case HAMath.VariogramType.Correlogram:

                    break;
                case HAMath.VariogramType.Covariance:

                    break;

                case HAMath.VariogramType.GeneralRelativeSemivariogram:

                    break;
                case HAMath.VariogramType.PairwiseRelativeSemivariogram:

                    break;
                case HAMath.VariogramType.Semimadogram:

                    break;
                case HAMath.VariogramType.Semirodogram:

                    break;
                case HAMath.VariogramType.Semivariogram:  //半方差函数.这个应该是相对好理解一点的，先从这个写起
                    this.CalSemiVariance( Points,  Values);

                    break;
                case HAMath.VariogramType.SemivariogramOfLogarithms:

                    break;
            }

            //this._variation = Variation;

        }


        /// <summary>
        /// 求离散点的半方差值
        /// </summary>
        /// <returns></returns>
        private void CalSemiVariance(List<KrigingPoint> Points, List<double> Values)
        {
            double[] SemiVariance = new double[this.LagNumber + 1];  //存储半方差值
            int[] DistancePair = new int[this.LagNumber + 1];  //存储每个距离的点的对数
            double[] SumSemiVariance = new double[this.LagNumber + 1]; //存储半方差总值

            double[] SumDistance = new double[this.LagNumber + 1];  //总距离
            double[] AverageDisantc = new double[this.LagNumber + 1];

            //以上三个字段index为0的值不用

            for (int i = 0; i < Points.Count - 1; i++)
            {

                //循环剩下的点，判断与当前点的关系
                for (int j = i + 1; j < Points.Count; j++)
                {                    
                    double temAngle;
                    if (Points[j].X != Points[i].X)
                    {
                        temAngle = Math.Atan((Points[j].Y - Points[i].Y) / (Points[j].X - Points[i].X));
                    }
                    else if (Points[j].Y > Points[i].Y)
                    {
                        temAngle = 90;
                    }
                    else
                    {
                        temAngle = -90;
                    }

                    if (temAngle > this.Angle + this.AngleTolerance || temAngle < this.Angle - this.AngleTolerance) continue; //此点不在当前点的角度容差范围内，不予考虑

                    //再判断此点是否在用户给定的带宽内，如果不在，不予考虑。暂时先不写代码，有点麻烦
                    if (this.Angle != 90)  //如果搜索角度为90度
                    { 
                    }


                    //=======================================================================================//
                    //如果此点符合用户角度上的要求
                    double distance = MathHleper.GetDistance(Points[i], Points[j]);
                    int index = (int)Math.Round(distance / this.LagDistance);

                    if (index > this.LagNumber) continue;
                    if (this.LagDistance * index + this.LagTolerance < distance || this.LagDistance * index - this.LagTolerance > distance) continue; //不符合用户滞后距的要求，不予考虑

                    double value = (Values[i] - Values[j]) * (Values[i] - Values[j]);
                    SumSemiVariance[index] = SumSemiVariance[index] + value;
                    DistancePair[index]++;
                    SumDistance[index] = SumDistance[index] + distance;

                }

            }

            for (int i = 0; i <= this.LagNumber; i++)
            {
                if (DistancePair[i] != 0)
                {
                    SemiVariance[i] = SumSemiVariance[i] / 2 / DistancePair[i];
                    AverageDisantc[i] = SumDistance[i] / DistancePair[i];
                }
                else
                {
                    SemiVariance[i] = 0;
                    AverageDisantc[i] = 0;
                }
            }

            this.Variation = SemiVariance;
            this.AverageDistance = AverageDisantc;

        }



        public object Clone()
        {
            return this.MemberwiseClone();
        }


    }
}
